#!/usr/bin/env python
# -*- coding: UTF-8 -*-
'''
@Project ：BroadviewCoss
@File    ：jobs.py
@IDE     ：PyCharm
@Author  ：kdly
@Date    ：2025/3/19 下午2:57
'''
import json
import logging

import threading
import time

from utils.redis_task import get_task_listener

from apscheduler.jobstores.base import JobLookupError
from apscheduler.triggers.cron import CronTrigger
from apscheduler.schedulers.background import BackgroundScheduler

from django.conf import settings
from django_apscheduler.jobstores import DjangoJobStore
from django_apscheduler.models import DjangoJobExecution
from django_apscheduler import util

from dashboard.models import MonitorTaskExecutionStatus
from monitor_alert_center.models import MonitorTask

logger = logging.getLogger(__name__)

# 创建全局调度器实例（单例模式）
scheduler = BackgroundScheduler(timezone=settings.TIME_ZONE)
scheduler.add_jobstore(DjangoJobStore(), "default")


def start_redis_listener():
    """启动Redis消息监听线程"""

    def _listen_loop():
        logger.info("正在启动Redis任务监听器...")
        redis = get_task_listener()
        pubsub = redis.pubsub()

        # 使用配置的频道名称
        pubsub.subscribe(settings.REDIS_TASK_CHANNEL)

        while True:
            try:
                message = pubsub.get_message(
                    ignore_subscribe_messages=True,
                    timeout=10  # 防止阻塞过久
                )
                if message:
                    handle_operation_message(message)
            except redis.ConnectionError:
                logger.warning("Redis连接丢失，5秒后重连...")
                time.sleep(5)
                redis = get_task_listener()  # 重新获取连接
            except Exception as e:
                logger.error(f"消息处理异常: {str(e)}")

    # 启动守护线程
    listener_thread = threading.Thread(
        target=_listen_loop,
        name="RedisTaskListener",
        daemon=True
    )
    listener_thread.start()


def handle_operation_message(message):
    """处理接收到的Redis消息"""
    try:
        data = json.loads(message['data'])
        action = data.get('action')
        task_id = data.get('task_id')

        if action not in ('enable', 'disable'):
            logger.warning(f"收到未知操作类型: {action}")
            return

        task = MonitorTask.objects.get(id=task_id)

        if action == 'disable':
            # 执行禁用逻辑
            if task.type in [1, 2]:
                delete_job(f"{task_id}__run_check_port_or_website_wrapper")
            elif task.type in [3, 4]:
                delete_job(f"{task_id}__run_cmd_wrapper")
            logger.info(f"已禁用任务: {task_id}")

        elif action == 'enable':
            # 执行启用逻辑
            from utils import run_task_and_send_alert_msg
            run_task_and_send_alert_msg.RunTaskAndSendAlertMsg().run_task(
                type=task.type,
                task_id=task_id,
                mon_obj=task.mon_path,
                mon_freq=task.mon_freq,
                host_id=task.host_id,
                timeout=task.timeout
            )
            logger.info(f"已启用任务: {task_id}")

    except MonitorTask.DoesNotExist:
        logger.error(f"任务不存在: {task_id}")
    except json.JSONDecodeError:
        logger.error(f"消息解析失败: {message}")
    except Exception as e:
        logger.error(f"处理消息异常: {str(e)}")


def init_scheduler():
    """初始化调度器，应在Django启动时调用"""
    start_redis_listener()
    if not scheduler.running:
        logger.info(f"正在启动计划任务...")
        scheduler.start()
        logger.info(f"计划任务启动成功...")

        mon_task_obj = MonitorTask.objects.filter(enable_status=1)
        from utils.run_task_and_send_alert_msg import RunTaskAndSendAlertMsg
        run_task_and_send_alert_msg = RunTaskAndSendAlertMsg()
        for item in mon_task_obj:
            task_id = ''
            if item.type == 1 or item.type == 2:
                task_id = f"{item.id}__run_check_port_or_website_wrapper"
            elif item.type == 3 or item.type == 4:
                task_id = f"{item.id}__run_cmd_wrapper"

            if not get_job(task_id):
                (run_task_and_send_alert_msg
                 .run_task(item.type, item.id, item.mon_path,
                           item.mon_freq, item.host_id, item.timeout))

        if not scheduler.get_job('delete_old_job_executions'):
            # 添加全局清理任务（只需添加一次）
            scheduler.add_job(
                delete_old_job_executions,
                trigger=CronTrigger(day_of_week="mon", hour="00", minute="00"),
                id="delete_old_job_executions",
                max_instances=1,
                replace_existing=True,
            )
            logger.warning("已添加周期清理任务：delete_old_job_executions")

        delete_job('get_monitor_task_status')
        if not scheduler.get_job('get_monitor_task_status'):
            # 添加全局任务（只需添加一次）
            scheduler.add_job(
                get_monitor_task_status,
                trigger=CronTrigger(minute=f"*/1"),
                id="get_monitor_task_status",
                max_instances=1,
                replace_existing=True,
            )

            logger.warning("已添加周期清理任务：get_monitor_task_status")


def get_job(job_id):
    """
    从队列中获取指定定时任务
    :param job_id: 任务唯一标识符（与添加时使用的id一致）
    :return: 该任务是否存在
    """
    try:
        job = scheduler.get_job(job_id)
        if job:
            return True
        return False
    except Exception as e:
        logger.warning(f"未找到对应任务: {job_id}。错误信息：{str(e)}")


def delete_job(job_id):
    """
    删除指定定时任务
    :param job_id: 任务唯一标识符（与添加时使用的id一致）
    :return: 是否删除成功
    """
    try:
        scheduler.remove_job(job_id)
        logger.info(f"成功删除任务: {job_id}")
        return True
    except JobLookupError as e:
        logger.warning(f"任务删除失败，未找到对应任务: {job_id}。错误信息：{str(e)}")
        return False


@util.close_old_connections
def delete_old_job_executions(max_age=604800):
    """
    此作业从数据库中删除早于“max_age”的APScheduler作业执行条目。它有助于防止数据库填满没有历史记录的旧历史记录更有用。
    ：param max_age：保留历史作业执行记录的最大时间长度。默认为7天。
    """
    DjangoJobExecution.objects.delete_old_job_executions(max_age)


def get_monitor_task_status():
    """获取监控计划任务的执行状态，并保存到->监控任务执行状态表"""
    task_obj = MonitorTask.objects.all()
    for item in task_obj:
        if item.enable_status == 1:
            if item.task_status == 1:
                MonitorTaskExecutionStatus.objects.create(
                    name_id=item.id,
                    type=item.type,
                    group_id=item.mon_group_id,
                    status=1
                )
            else:
                MonitorTaskExecutionStatus.objects.create(
                    name_id=item.id,
                    type=item.type,
                    group_id=item.mon_group_id,
                    status=0
                )


def schedule_job(func_job, minute, task_id, args=()):
    """
    调度传入的 func_job，根据 cron_expression 执行。
    :param func_job: 要执行的函数
    :param minute: 分钟数，10,则每10分钟一次
    :param task_id: 任务ID
    :param args: 需要传给被执行的计划任务的实参
    """
    try:
        if not task_id:
            return
        job_id = f"{task_id}_{func_job.__name__}"
        trigger = CronTrigger(minute=f"*/{minute}")
        if get_job(job_id):
            delete_job(job_id)
            logger.warning(f"已删除job：{job_id}")

        scheduler.add_job(
            func_job,
            trigger=trigger,
            args=args,
            id=job_id,
            max_instances=1,
            replace_existing=True,
        )
        logger.info(f"添加计划任务 '{job_id}'.")
    except Exception as e:
        logger.error(f"添加计划任务失败: {job_id}...")
        raise e
